#include "utf8.h"
#include <stdint.h>
#include <assert.h>

/* find out how many high-order 1.
 * Using a little hack ^_^ */
static int utf8_char_length_pow2(unsigned char* utf8_string)
{
    uint8_t leader = utf8_string[0];
    leader = ~leader;
    leader |= leader >> 1;
    leader |= leader >> 2;
    leader |= leader >> 4;
    leader = (leader >> 1) + 1;
    return leader;

}

int utf8_char_length(unsigned char* utf8_string)
{
    if (utf8_string[0] < 128)
        return 1;
    else if ( (utf8_string[0]&0xc0) == 0x80 )
        /* meet 10xxxxxx, is not the leading byte */
        return 0;
    else {

        uint8_t leader = utf8_char_length_pow2(utf8_string);
        switch (leader) {
            case 0x20:
                return 2;
            case 0x10:
                return 3;
            case 0x08:
                return 4;
            case 0x04:
                return 5;
            case 0x02:
                return 6;
            default:
                return 0;
        }
    }
}

uint64_t get_unicode_char(unsigned char *utf8_string)
{
    unsigned char* byte_seq = utf8_string;
    int len = utf8_char_length(byte_seq);
    int i;
    assert(len != 0);
    if (len == 1)
        return byte_seq[0];
    else {
        uint8_t leading = utf8_char_length_pow2(byte_seq);
        uint8_t mask = leading - 1;

        uint64_t result = 0;
        /* handle the leading byte */
        result = byte_seq[0] & mask;

        /* handle the rest */
        mask = 0x3f;
        for ( i = 1; i < len; ++i)
            result = (result << 6) | (byte_seq[i] & mask);

        return result;
    }
}

unsigned char* next_utf8_char(unsigned char *utf8_string)
{
    return utf8_string+utf8_char_length(utf8_string);
}
